import {isDef} from "../../shared/util";

/**
 * 对该组件的监听事件进行了初始化和事件代理
 * @param {Reactive} vm
 */
export function initEvents (vm) {
    // 组件的监听事件
    vm._events = {};
    // 如果组件被挂载监听事件，在实例上注册监听事件
    if (isDef(vm.$options.listeners)) {
        vm.listeners = vm.$options.listeners;
        Object.keys(vm.listeners).forEach(function (event) {
            const handlers = vm.listeners[event];
            vm.$on(event, handlers);
        });
    }
}

export function eventsMixin (Reactive) {

    /**
     * 为 vm 挂载 event 对应的事件监听函数
     * @param {String} event
     * @param {Array<Function>} handlers
     */
    Reactive.prototype.$on = function (event, handlers) {
        isDef(this._events[event])
            ? handlers.forEach((handler) => {
                this._events[event].handlers.push(handler);
            })
            : this._events[event] = createInvoker(this, handlers);
    };

    /**
     * 卸载 vm 上所有 event 事件监听函数
     * @param {String} event
     */
    Reactive.prototype.$off = function (event) {
        if (isDef(this._events[event])) {
            this._events[event].clear();
            delete this._events[event];
        }
    };

    /**
     * 触发组件占位节点上的挂载事件
     * @param {String} event
     * @param {Array<*>} args
     */
    Reactive.prototype.$emit = function (event, ...args) {
        if (isDef(this._events[event])) {
            const invoker = this._events[event];
            try{
                invoker.call(this, event, ...args);
            }
            catch (e) {
                console.warn(e);
            }
        }
        else {
            throw new Error(`组件无${event}类型事件`);
        }
    };

}

/**
 * 封装一个代理函数
 * @param {Reactive} vm
 * @param {Array<Function>} handlers
 * @return {Function}
 */
export function createInvoker (vm, handlers) {
    const invoker = function (event, ...args) {
        for (let i=0; i<invoker.handlers.length; i++) {
            const handler = invoker.handlers[i];
            handler.apply(vm, [event, ...args]);
        }
    };
    invoker.handlers = handlers;
    return invoker;
}

